package cn.yh.nft.turbo.order.infrastructure.sharding.algorithm;

import cn.yh.nft.turbo.order.sharding.id.DistributeID;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.shardingsphere.sharding.api.sharding.complex.ComplexKeysShardingAlgorithm;
import org.apache.shardingsphere.sharding.api.sharding.complex.ComplexKeysShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.hint.HintShardingAlgorithm;
import org.apache.shardingsphere.sharding.api.sharding.hint.HintShardingValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.HashSet;
import java.util.Properties;
import java.util.stream.Collectors;

/**
 * @author 草莓爱上啵啵
 */
public class TurboKeyShardingAlgorithm implements ComplexKeysShardingAlgorithm<String>, HintShardingAlgorithm<String> {

    private static Logger logger = LoggerFactory.getLogger(TurboKeyShardingAlgorithm.class);

    private Properties props;

    private static final String PROP_MAIN_COLUM = "mainColum";

    private static final String PROP_TABLE_COUNT = "tableCount";

    @Override
    public Properties getProps() {
        return props;
    }

    @Override
    public void init(Properties props) {
        this.props = props;
    }

    /**
 * 根据复杂的键值对进行分片
 *
 * @param availableTargetNames 可用的目标名称集合，通常代表可以分片的表或库的名称
 * @param complexKeysShardingValue 包含分片所需键值对的复杂类型
 * @return 返回根据分片逻辑计算出的目标名称集合如果无法进行有效的分片，则返回null
 */
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames, ComplexKeysShardingValue<String> complexKeysShardingValue) {
    Collection<String> result = new HashSet<>();

    // 获取主分片列的名称
    String mainColum = props.getProperty(PROP_MAIN_COLUM);
    // 获取分片键的值
    Collection<String> mainColums = complexKeysShardingValue.getColumnNameAndShardingValuesMap().get(mainColum);

    // 如果主分片列的值不为空，则根据这些值计算分片目标
    if (CollectionUtils.isNotEmpty(mainColums)) {
        for (String colum : mainColums) {
            String shardingTarget = calculateShardingTarget(colum);
            result.add(shardingTarget);
        }
        // 返回与计算出的分片目标匹配的表集合
        return getMatchedTables(result, availableTargetNames);
    }

    // 如果主分片列的值为空，则移除主分片列，并尝试使用其他列进行分片
    complexKeysShardingValue.getColumnNameAndShardingValuesMap().remove(mainColum);
    Collection<String> otherColums = complexKeysShardingValue.getColumnNameAndShardingValuesMap().keySet();
    // 如果其他列不为空，则遍历这些列及其值，计算分片目标
    if (CollectionUtils.isNotEmpty(otherColums)) {
        for (String colum : otherColums) {
            Collection<String> otherColumValues = complexKeysShardingValue.getColumnNameAndShardingValuesMap().get(colum);
            for (String value : otherColumValues) {
                String shardingTarget = extractShardingTarget(value);
                result.add(shardingTarget);
            }
        }
        // 返回与计算出的分片目标匹配的表集合
        return getMatchedTables(result, availableTargetNames);
    }

    // 如果无法根据任何列进行分片，则返回null
    return null;
}


    private Collection<String> getMatchedTables(Collection<String> results, Collection<String> availableTargetNames) {
        Collection<String> matchedTables = new HashSet<>();
        for (String result : results) {
            matchedTables.addAll(availableTargetNames.parallelStream().filter(each -> each.endsWith(result)).collect(Collectors.toSet()));
        }
        return matchedTables;
    }

    private String extractShardingTarget(String orderId) {
        return DistributeID.getShardingTable(orderId);
    }

    private String calculateShardingTarget(String buyerId) {
        String tableCount = props.getProperty(PROP_TABLE_COUNT);
        return DistributeID.getShardingTable(buyerId, Integer.parseInt(tableCount));
    }

    @Override
    public Collection<String> doSharding(Collection<String> collection, HintShardingValue<String> hintShardingValue) {
        logger.info("collection : " + collection);
        logger.info("hintShardingValue : " + hintShardingValue);
        String logicTableName = hintShardingValue.getLogicTableName();
        Collection<String> shardingTargets = hintShardingValue.getValues();

        Collection<String> matchedTables = new HashSet<>();
        for (String shardingTarget : shardingTargets) {
            matchedTables.add(logicTableName + "_" + shardingTarget);
        }

        logger.info("matchedTables : " + matchedTables);
        return CollectionUtils.intersection(collection, matchedTables);
    }
}
